/*
    Contact info:
    bhepple@freeshell.org
    http://bhepple.freeshell.org/scrmgr

    Copyright (C) 1991-2008 Bob Hepple

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; see the file COPYING.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.

*/
/*
 * smproj.c: Front-end to the SCRMGR system.
 *
 * This program paints a screen with just one field
 * and a bunch of softkey labels, not entirely unlike this: 

--------------------------------------------------------------------------------
                        Screen Manager Shell



            Project name:  ###################^






Screen   Data                                                          Exit    
Editor   Entry                                                                 
--------------------------------------------------------------------------------

 * The program then waits for user input.  The user types a valid
 * project name into the field (denoted by #################) and then
 * presses the appropriate softkey. The program is passed the 'event'
 * generated by the softkey and takes appropriate action.

 * This is probably about as simple as a SCRMGR program gets - just a
 * little up from 'Hello World' but it illustrates the simple callback
 * stack and event processing under SCRMGR.

 * Note that 'pascal' ordering of routines is used here - i.e. main() comes
 * last.

 * All the 'handles' to objects defined by SCRMGR have a name prefix
 * of "SM_".

 * Note on compiling: The above screen would have been developed with
 * smcreate(1) the interactive screen design program and a component of
 * SCRMGR. The resulting screen file in this case was called
 * "smproj.dat". From this screen definition, two dependant files are
 * generated automatically by the sm2c(1) program called "smproj.opn"
 * and "smproj.def". The .def file contains C object definitions for
 * the applications and the .opn file contains C code to initialise
 * them.  These three files, .dat, .opn and .def are all included at
 * appropriate places in the code.
 */

/**************************************************************************
 * The parameters to routines are coded like:				  *
 *									  *
 * int proc ARG2(int, argc, char **, argv) { ... }			  *
 *									  *
 * This allows the compiler to take advantage of any prototype		  *
 * checking that may be available as well as allowing the code to	  *
 * compile on very early C compilers, depending on how ARG2 is		  *
 * defined, e.g. if prototype checking is available then the above	  *
 * would be expanded to 						  *
 *									  *
 * 	int proc(type1 arg1, type2 arg2)				  *
 *									  *
 * If not, it would expand to 						  *
 *									  *
 * 	int proc(arg1, arg2) 					  	  *
 *     	    type1 arg1; type2 arg2; ... i.e. the old style		  *
 **************************************************************************/

#include <stdio.h>
#include <getopt.h>
#include <setjmp.h>

#include "smlib.h"
#include "smio.h"
#include "smproj.def"

CHAR *smproj_rcsid(void) {
    return("$Id: smproj.c,v 1.6 2008/02/19 13:15:50 bhepple Exp $");
}

/* Globals */
static CHAR	rev_string[] = "$Revision: 1.6 $";
jmp_buf		NoMoreHeap;

/* Handles to SCRMGR objects used globally */
SM_FILE		screenset;
SM_SCREEN  	main_screen;
SM_SOFTKEY  	main_keys;
SM_FIELD	project_name_fld;

/**************************************************************************
 * SCRMGR programs can have their associated screens compiled into the 	  *
 * binary or left as ascii files to be read at runtime. This allows	  *
 * faster modification while developing the application if		  *
 * "READ_DAT_AT_RUNTIME" is defined so that small changes to a screen	  *
 * definition file does not require a compile.  When the application	  *
 * is frozen and deployed then it should probably have the screen	  *
 * files compiled in for safety.					  *
 **************************************************************************/

#if !READ_DAT_AT_RUNTIME
static CHAR *smproj_dat[] = {
#include "smproj.dat"
NULL,
};
#endif

static CHAR *usage_msg[] = {
"SCRMGR project front end program.",
"project: project name (usually the basename of a file - no '.' or extension)",
NULL
};

void usage()
{
    CHAR **c;
    
    fprintf(stderr, "Revision %s of %s.\n", rev_string, sm_program_name);
    fprintf(stderr, "Usage: %s [project]\n", sm_program_name);
    
    c = usage_msg;
    while (*c) {
        fputs(*c, stderr);
        fputc('\n', stderr);
        c++;
    }
    exit(2);
}

/************************************************************************** 
 * Fields can be registered for special handling by user-provided	  *
 * routines.  The following routine is provided as the gateway through	  *
 * which the routines are registered. This facility is not used in	  *
 * this program, but an entry point must be provided.			  *
 **************************************************************************/

f_ptr sm_get_field_trigger ARG1(SM_FIELD, field)
{
    return(NULL);
}

/**************************************************************************
 * Termination code						  	  *
 **************************************************************************/
void sm_error_exit ARG1(CHAR *, msg)
{
    sm_term();
    puts(msg);
    exit(1);
}

/**************************************************************************
 * Here is how you can fork out to an external program in SCRMGR. All	  *
 * the messing around with terminal modes & states is taken care of.	  *
 **************************************************************************/
static void runit ARG2(CHAR *, program, CHAR *, project)
{
    CHAR	*buf = alloca(MAXPATHLEN);

    strcpy(buf, program);
    strcat(buf, " ");
    strcat(buf, project);
    if (!strchr(project, '.'))
	strcat(buf, ".dat");
    system(buf);
    smio_refresh(); /* restores terminal to curses modes */
}

/**************************************************************************
 * This routine simply prepares the screen for display and data-entry.	  *
 **************************************************************************/
static void prep_main_loop ()
{
    sm_setup_scr(SM_BASE_WINDOW, main_screen, NULL, NULL, 0, 0, 0);
}

/**************************************************************************
 * This is a callback which is registered later on in the code.  It	  *
 * receives interesting events from the SCRMGR event control loop.  If	  *
 * the event is an exit command, then pass it back to SCRMGR for action.  *
 * Some events, like SCREEN_EDITOR have been created by the smcreate(1)	  *
 * program when the original form was done. Note that when		  *
 * this routine is entered, the current screen has been 'popped off' the  *
 * event stack and needs to be re-initialised with sm_setup_scr(3).	  *
 **************************************************************************/

CALLBACK(smproj_main_loop)
{
    alloca(0);

    switch(command) {
    case SCREEN_EDITOR:
	runit("smcreate", sm_fld_enq(project_name_fld));
	break;

    case DATA_ENTRY:
	runit("smdata", sm_fld_enq(project_name_fld));
	break;

    case EXIT:
    case SM_EXIT_C:
    case SM_NO_SAVE_EXIT_C:
	return(command);

    default:
	break;
    }
    
    prep_main_loop();
    return(0);
}

int main ARG2(int, argc, CHAR **, argv)
{
    int		c;

/**************************************************************************
 * Preparation: let SCRMGR look at the arguments (but not change them) 	  *
 **************************************************************************/
    sm_pre_init(argc, argv);    

/**************************************************************************
 * Now go: if starting into X, then some arguments may be removed 	  *
 **************************************************************************/
    sm_init(1, argc, argv);

/**************************************************************************
 * Process any program specific arguments - in this case, there		  *
 * are none.								  *
 **************************************************************************/
    while ((c = getopt(argc, argv, "?")) != EOF)
        switch (c) {
	    
        default:
            sm_term();
	    usage(); 
	    break;
        }

/**************************************************************************
 * Emergency back door in case of memory management problems - safety	  *
 * first, really, one could do some recovery here if necessary.		  *
 **************************************************************************/
    if (setjmp(NoMoreHeap)) {
	sm_message(sm_mem_error);
	sm_term();
	exit(1);
    }

/**************************************************************************
 * Now get the screen file - either from the compiled-in table or from	  *
 * the external file.							  *
 **************************************************************************/
    screenset = sm_user_screens();

#if READ_DAT_AT_RUNTIME
    {
	CHAR	buf[MAXPATHLEN + 1];

	buf[0] = 0;
#if NEEDS_PATH_FOR_PROGS
	strcpy(buf, sm_program_directory);
#endif
	strcat(buf, "smproj.dat");
	if (sm_fopen(screenset, buf) == SM_ERROR)
	    sm_error_exit("Error reading smproj.dat!");
    }
#else
    if (sm_dbopen(screenset, smproj_dat) == SM_ERROR)
	sm_error_exit("Error reading internal data block!");
#endif

/**************************************************************************
 * Now open the appropriate screens ("main_screen") and register any	  *
 * special keys/functions 						  *
 **************************************************************************/
#include "smproj.opn"

/**************************************************************************
 * Get the main_screen ready for display - essentially blank all fields.  *
 **************************************************************************/
    sm_scr_init(main_screen, /* recursive = */ 1);

/**************************************************************************
 * Get a handle to the field called "project_name_fld" 			  *
 **************************************************************************/
    if ((project_name_fld = sm_fld_open(main_screen, "project_name_fld")) == NULL)
	exit(1);

/**************************************************************************
 * Set the field's value to the first argument, if present.		  *
 **************************************************************************/
    if ((optind >= argc) || (strlen(argv[optind]) == 0))
	;
    else 
	sm_fld_set(project_name_fld, argv[optind++]);

/**************************************************************************
 * Put a simple one-line message on the screen - just the revision of the *
 * program, in this case.						  *
 **************************************************************************/
    sm_message(rev_string);

/**************************************************************************
 * Tell SCRMGR that events are to be handled by smproj_main_loop.	  *
 **************************************************************************/
    sm_add_callback(smproj_main_loop, NULL);

/**************************************************************************
 * display the screen.	  						  *
 **************************************************************************/
    prep_main_loop();

/**************************************************************************
 * ... and pass control to SCRMGR.  Control will pass to                  *
 * "smproj_main_loop" for any appropriate events.                         *
 **************************************************************************/
    sm_main_loop();

/**************************************************************************
 * When we get here then it's all over - just terminate. 		  *
 **************************************************************************/
    sm_term();
    exit(0);
}


/* For emacs:
 * Local Variables:
 * tab-width: 8
 * End:
 */
